﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MetaCollection.Framework.Model;
using MetaCollection.Framework.Configuration;
using MetaCollection.Framework.Repository;
using System.Diagnostics;
using System.Globalization;

namespace MetaCollection.Framework.Provider
{
	public class AuditProvider : IProvider
	{
		private string m_Description = "Provides Item Auditing Functionality.";
		private Guid m_Id = new Guid("{454C1903-9E23-4e82-BBB0-0F6E97EF4960}");
		private string m_Name = "AuditProvider";
		private RequestEventTypes m_Events = RequestEventTypes.OnProvideItem | RequestEventTypes.OnProvideResource;
		
		
		/// <summary>
		/// Extracts the context.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		/// <returns></returns>
		private ItemContext ExtractContext(object sender, EventArgs args)
		{
			ItemContext context = sender as ItemContext;
			if (context == null)
			{
				throw new InvalidOperationException("The Current Request Context of this request can not be null.");
			}
			return context;
		}

		/// <summary>
		/// Logs the event.
		/// </summary>
		/// <param name="source">The source.</param>
		/// <param name="sender">The sender.</param>
		/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void LogAudit(string source, object sender, EventArgs args)
		{
			ItemContext context = this.ExtractContext(sender, args);
			if (context.RequestType.HasType(RequestTypes.DeleteItem))
			{
				context.AuditRepository.SaveItemEvent(context.RequestedItem, "Delete", string.Format(CultureInfo.InvariantCulture, "{0}:Item {1} version {2} deleted.", source, context.RequestedItem.Id, context.RequestedItem.Version));
			}
			if (context.RequestType.HasAnyType(RequestTypes.PostToItem | RequestTypes.SetItem | RequestTypes.SetResource))
			{
				IItemInstance item = context.ResponseItems.FirstOrDefault();
				context.AuditRepository.SaveItemEvent(item, context.RequestType.ToString(), context.Changes != null ?context.Changes.ToString() : "Change Set not recorded");
			}
		}
		
		#region IProvider Members

		/// <summary>
		/// Gets the friendly description of this provider.
		/// </summary>
		/// <value>The description.</value>
		public string Description{ get { return this.m_Description; } }

		/// <summary>
		/// Unique Identifier of this provider.
		/// </summary>
		/// <value></value>
		public Guid Id { get { return this.m_Id; } }

		/// <summary>
		/// Gets the name of this provider.
		/// </summary>
		/// <value>The name.</value>
		public string Name{ get { return this.m_Name; } }

		/// <summary>
		/// Gets the Request Events that are supported by this provider.
		/// </summary>
		/// <value>RequestEventTypes supported by this provider</value>
		public RequestEventTypes SupportedEvents { get { return m_Events; } set { m_Events = value; } }

		/// <summary>
		/// Initializes the provider specified in the ProviderType.
		/// </summary>
		/// <param name="providerConfig">The provider config.</param>
		/// <exception cref="System.ArgumentNullException">The name of the provider is null or one of the Repositories is null and it can not be created internally.</exception>
		/// <exception cref="System.ArgumentException">The name of the provider has a length of zero</exception>
		/// <exception cref="System.InvalidOperationException"> An attempt is made to call
		/// MetaCollection.Framework.Provider.IProvider.Initialize(MetaCollection.Framework.Model.IProviderType)
		/// on a provider after the provider has already been initialized</exception>
		public void Initialize(ProviderReferenceElement providerConfig)
		{
			if (providerConfig == null)
			{
				throw new ArgumentNullException("providerConfig");
			}
			if (string.IsNullOrEmpty(providerConfig.Name))
			{
				throw new ArgumentException("Name of Provider can not be null", "providerConfig");
			}
			m_Name = providerConfig.Name;

			RequestEventTypes configTypes = RequestEventTypes.None;
			if (!string.IsNullOrWhiteSpace(providerConfig.Text) && Enum.TryParse<RequestEventTypes>(providerConfig.Text, true, out configTypes))
				SupportedEvents = configTypes;
		}		

		#endregion

		#region IRequestSink Members

		public void OnBeforeInvokeRequest(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnBeforeInvokeRequest))
				LogAudit("OnBeforeInvokeRequest", sender, args);
		}

		public void OnBeforePathResolve(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnBeforePathResolve))
				LogAudit("OnBeforePathResolve", sender, args);
		}

		public void OnBeforeValidateRequest(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnBeforeValidateRequest))
				LogAudit("OnBeforeValidateRequest", sender, args);
		}

		public void OnHandlerDisposing(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnHandlerDisposing))
				LogAudit("OnHandlerDisposing", sender, args);
		}

		public void OnPathResolve(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnPathResolve))
				LogAudit("OnPathResolve", sender, args);
		}

		public void OnPathResolveComplete(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnPathResolveComplete))
				LogAudit("OnPathResolveComplete", sender, args);
		}

		public void OnProvideItem(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnProvideItem))
				LogAudit("OnProvideItem", sender, args);
		}

		public void OnProvideResource(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnProvideResource))
				LogAudit("OnProvideResource", sender, args);
		}

		public void OnRequestComplete(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnRequestComplete))
				LogAudit("OnRequestComplete", sender, args);
		}

		public void OnRequestStart(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnRequestStart))
				LogAudit("OnRequestStart", sender, args);
		}

		public void OnRequestValidateComplete(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnRequestValidateComplete))
				LogAudit("OnRequestValidateComplete", sender, args);
		}

		public void OnValidateRequest(object sender, EventArgs args)
		{
			if (m_Events.HasFlag(RequestEventTypes.OnValidateRequest))
				LogAudit("OnValidateRequest", sender, args);
		}

		

		
		#endregion
	}
}
